package net.lab1024.sa.admin.config;

import org.flowable.bpmn.model.*;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;

public class CustomProcessDiagramGenerator implements ProcessDiagramGenerator {

    // 样式常量
    private static final Color HIGHLIGHT_NODE_BORDER = new Color(255, 69, 0);
    private static final Color HIGHLIGHT_NODE_FILL = new Color(255, 240, 230, 100);
    private static final Color HIGHLIGHT_FLOW = new Color(255, 0, 0);
    private static final float HIGHLIGHT_WIDTH = 2.5f;
    private static final Color NORMAL_NODE_BORDER = Color.BLACK;
    private static final Color NORMAL_NODE_FILL = Color.WHITE;
    private static final String CHINESE_FONT = "SimHei";

    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivityIds,
                                       List<String> highLightedFlowIds, String activityFontName, String labelFontName,
                                       String annotationFontName, ClassLoader customClassLoader, double scaleFactor,
                                       boolean drawSequenceFlowNameWithNoLabelDI) {
        try {
            // 1. 计算画布尺寸
            int[] bounds = calculateCanvasBounds(bpmnModel);
            int width = bounds[0], height = bounds[1];
            int margin = 50;
            double minX = bounds[2], minY = bounds[3];

            // 2. 创建图像
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, width, height); // 白色背景

            // 3. 绘制连线（先画连线，避免被节点遮挡）
            drawSequenceFlows(g, bpmnModel, highLightedFlowIds, minX, minY, margin);

            // 4. 绘制节点
            drawFlowNodes(g, bpmnModel, highLightedActivityIds, minX, minY, margin);

            // 5. 释放资源
            g.dispose();

            // 6. 转换为输入流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(image, imageType, out);
            return new ByteArrayInputStream(out.toByteArray());

        } catch (Exception e) {
            throw new RuntimeException("生成流程图失败", e);
        }
    }

    // 绘制所有节点
    private void drawFlowNodes(Graphics2D g, BpmnModel bpmnModel, List<String> highLightedActivityIds,
                               double minX, double minY, int margin) {
        for (FlowElement element : bpmnModel.getMainProcess().getFlowElements()) {
            if (element instanceof FlowNode) {
                FlowNode node = (FlowNode) element;
                boolean isHighlighted = highLightedActivityIds != null && highLightedActivityIds.contains(node.getId());

                // 使用 GraphicInfo 获取节点坐标（适配 Flowable 7.0.0）
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(node.getId());
                if (graphicInfo == null) continue;

                int x = (int) (graphicInfo.getX() - minX + margin);
                int y = (int) (graphicInfo.getY() - minY + margin);
                int w = (int) graphicInfo.getWidth();
                int h = (int) graphicInfo.getHeight();

                // 绘制节点形状
                Shape shape = getNodeShape(node, x, y, w, h);
                g.setColor(isHighlighted ? HIGHLIGHT_NODE_FILL : NORMAL_NODE_FILL);
                g.fill(shape);
                g.setColor(isHighlighted ? HIGHLIGHT_NODE_BORDER : NORMAL_NODE_BORDER);
                g.setStroke(new BasicStroke(isHighlighted ? HIGHLIGHT_WIDTH : 1.0f));
                g.draw(shape);

                // 绘制标签
                drawNodeLabel(g, node.getName(), x, y, w, h);
            }
        }
    }

    // 绘制所有连线
    private void drawSequenceFlows(Graphics2D g, BpmnModel bpmnModel, List<String> highLightedFlowIds,
                                   double minX, double minY, int margin) {
        for (FlowElement element : bpmnModel.getMainProcess().getFlowElements()) {
            if (element instanceof SequenceFlow) {
                SequenceFlow flow = (SequenceFlow) element;
                boolean isHighlighted = highLightedFlowIds != null && highLightedFlowIds.contains(flow.getId());

                // 获取连线的起点和终点节点ID
                String sourceRef = flow.getSourceRef();
                String targetRef = flow.getTargetRef();
                if (sourceRef == null || targetRef == null) continue;

                // 通过ID查找对应的FlowNode对象
                FlowElement sourceElement = bpmnModel.getMainProcess().getFlowElement(sourceRef);
                FlowElement targetElement = bpmnModel.getMainProcess().getFlowElement(targetRef);

                if (!(sourceElement instanceof FlowNode) || !(targetElement instanceof FlowNode)) {
                    continue;
                }

                FlowNode source = (FlowNode) sourceElement;
                FlowNode target = (FlowNode) targetElement;

                // 使用 GraphicInfo 计算连线坐标（适配 Flowable 7.0.0）
                GraphicInfo sourceInfo = bpmnModel.getGraphicInfo(source.getId());
                GraphicInfo targetInfo = bpmnModel.getGraphicInfo(target.getId());
                if (sourceInfo == null || targetInfo == null) continue;

                int x1 = (int) (sourceInfo.getX() + sourceInfo.getWidth()/2 - minX + margin);
                int y1 = (int) (sourceInfo.getY() + sourceInfo.getHeight()/2 - minY + margin);
                int x2 = (int) (targetInfo.getX() + targetInfo.getWidth()/2 - minX + margin);
                int y2 = (int) (targetInfo.getY() + targetInfo.getHeight()/2 - minY + margin);

                // 绘制连线
                g.setColor(isHighlighted ? HIGHLIGHT_FLOW : Color.BLACK);
                g.setStroke(new BasicStroke(isHighlighted ? HIGHLIGHT_WIDTH : 1.0f));
                g.draw(new Line2D.Double(x1, y1, x2, y2));

                // 绘制连线标签
                drawFlowLabel(g, flow.getName(), x1, y1, x2, y2);
            }
        }
    }

    // 绘制节点标签
    private void drawNodeLabel(Graphics2D g, String label, int x, int y, int w, int h) {
        if (label == null || label.isEmpty()) return;

        Font originalFont = g.getFont();
        g.setFont(new Font(CHINESE_FONT, Font.BOLD, 12));
        FontMetrics metrics = g.getFontMetrics();
        Rectangle2D textBounds = metrics.getStringBounds(label, g);

        int textX = x + (w - (int) textBounds.getWidth()) / 2;
        int textY = y + (h + (int) textBounds.getHeight()) / 2 - 5;

        g.setColor(Color.BLUE);
        g.drawString(label, textX, textY);
        g.setFont(originalFont);
    }

    // 绘制连线标签
    private void drawFlowLabel(Graphics2D g, String label, int x1, int y1, int x2, int y2) {
        if (label == null || label.isEmpty()) return;

        Font originalFont = g.getFont();
        g.setFont(new Font(CHINESE_FONT, Font.ITALIC, 10));

        int x = (x1 + x2) / 2;
        int y = (y1 + y2) / 2 - 10; // 在线上方

        g.setColor(Color.DARK_GRAY);
        g.drawString(label, x, y);
        g.setFont(originalFont);
    }

    // 获取节点形状
    private Shape getNodeShape(FlowNode node, int x, int y, int w, int h) {
        if (node instanceof StartEvent || node instanceof EndEvent) {
            return new Ellipse2D.Double(x, y, w, h); // 事件：圆形
        } else if (node instanceof Gateway) {
            return new Rectangle2D.Double(x, y, w, h); // 网关：菱形（简化为矩形）
        } else {
            return new Rectangle2D.Double(x, y, w, h); // 任务：矩形
        }
    }

    // 计算画布边界（适配 Flowable 7.0.0）
    private int[] calculateCanvasBounds(BpmnModel bpmnModel) {
        double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE;
        double maxX = Double.MIN_VALUE, maxY = Double.MIN_VALUE;

        for (FlowElement element : bpmnModel.getMainProcess().getFlowElements()) {
            if (element instanceof FlowNode) {
                FlowNode node = (FlowNode) element;
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(node.getId());
                if (graphicInfo != null) {
                    minX = Math.min(minX, graphicInfo.getX());
                    minY = Math.min(minY, graphicInfo.getY());
                    maxX = Math.max(maxX, graphicInfo.getX() + graphicInfo.getWidth());
                    maxY = Math.max(maxY, graphicInfo.getY() + graphicInfo.getHeight());
                }
            }
        }

        int margin = 50;
        return new int[]{
                (int) (maxX - minX + 2 * margin),
                (int) (maxY - minY + 2 * margin),
                (int) minX,
                (int) minY
        };
    }

    // 实现所有必需的接口方法
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivityIds,
                                       List<String> highLightedFlowIds, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivityIds, highLightedFlowIds,
                null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivityIds,
                                       List<String> highLightedFlowIds, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivityIds, highLightedFlowIds,
                null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivityIds,
                                       boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivityIds, null,
                null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivityIds,
                                       double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivityIds, null,
                null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader,
                                       boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, null, null,
                activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader,
                                       double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, null, null,
                activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generatePngDiagram(BpmnModel bpmnModel, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, "png", null, null,
                null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, "png", null, null,
                null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public InputStream generateJpgDiagram(BpmnModel bpmnModel) {
        return generateDiagram(bpmnModel, "jpg", null, null,
                null, null, null, null, 1.0, true);
    }

    @Override
    public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, "jpg", null, null,
                null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    @Override
    public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) {
        try {
            InputStream is = generateDiagram(bpmnModel, "png", null, null,
                    null, null, null, null, scaleFactor, true);
            return ImageIO.read(is);
        } catch (Exception e) {
            throw new RuntimeException("生成PNG图像失败", e);
        }
    }
}
